#!/usr/bin/python
import sys
import os
import datetime
import MySQLdb
from mysql_analyzer import config
from mysql_analyzer import input
from mysql_analyzer import output

    
if __name__ == "__main__":

    try:
        # Reads the configuration file and parse the command line arguments.
        config = config.Config()
        #date
        log_date = datetime.datetime.now()
        config.setAction("Connecting to MySQL database")
        
        conn = MySQLdb.Connection(user=config.database_user, passwd=config.database_password,host= config.database_host, db=config.database_name)
        
        cursor = conn.cursor()
        
        if config.database_type.lower()=="mysql":
            cursor.execute('SET autocommit=0;')
        
        dbInput = input.DBInput(config, cursor)
        dbOutput = output.DBOutput(config, cursor)
        
        config.setAction("Mysql security audit tests will be run now.")
        
        for audit_item in config.audit_parameters_list:
            
            config.setAction("Launching security audit test - '%s' " %audit_item['name'])
            result = dbInput.getTestResult(audit_item['command'])
            #print result

            # MySQL version test
            if audit_item['name'].find('find mysql version')>=0:
                if result:
                    config.setAction("MySQL version information:")
                    for item in result:
                        config.setAction("%s:%s"%(item,result[item]))

            # MySQL anonymous accounts 
            if audit_item['name'].find('find anonymous user accounts')>=0:
                if result:
                    config.setAction("MySQL anonymous accounts information:")
                    for item in result:
                        config.setAction("user - %s and host - %s" %(item[0],result[item]))
                    config.logger.error("Presence of anonymous accounts on the mysql server is detected. This poses a high severity security risk to the database and MUST be corrected as early as possible.")
            
            #look for MySQL accounts accessible over any host
            if audit_item['name'].find('find accounts accessible from any host')>=0:
                if result:
                    config.setAction("MySQL server is accessible over the following hosts:")
                    for item in result:
                        config.setAction("user - %s and host - %s" %(item[0],result[item]))
                else:
                    config.setAction("MySQL server is not accessible over any host other than localhost!")
            
            # find accounts with empty passwords
            if audit_item['name'].find('find accounts with empty passwords')>=0:
                if result:
                    config.setAction("MySQL server is having following accounts with no passwords:")
                    for item in result:
                        config.setAction("user - %s and host - %s" %(item[0],result[item]))
                else:
                    config.setAction("MySQL server is not having any accounts with out password!")
            # find accounts with admin privileges
            if audit_item['name'].find('find admin accounts')>=0:
                #print result
                if result:
                    config.setAction('MySQL server is having following accounts with administrative privileges:')
                    for item in result:
                        config.setAction("grantee - %s and privilege - %s" %(item,result[item]))
                else:
                    config.setAction("MySQL server is not having any accounts with administrative privileges!")
                    
            # find accounts with data definition privileges
            if audit_item['name'].find('find accounts with data definition privileges')>=0:
                if result:
                    config.setAction('MySQL server is having following accounts with data definition privileges:')
                    for item in result:
                        config.setAction("grantee - %s and privilege - %s" %(item,result[item]))
                else:
                    config.setAction("MySQL server is not having any accounts with data definition privileges!")
                    
            # find accounts with data manipulation privileges
            if audit_item['name'].find('find accounts with data manipulation privileges')>=0:
                if result:
                    config.setAction('MySQL server is having following accounts with data manipulation privileges:')
                    for item in result:
                        config.setAction("grantee - %s and privilege - %s" %(item,result[item]))
                else:
                    config.setAction("MySQL server is not having any accounts with data manipulation privileges!")
                    
            #check if Mysql running in chroot environment
            if audit_item['name'].find('find whether mysql running in chroot')>=0:
                if result:
                    config.setAction("MySQL server is running in chroot environment.")
                else:
                    config.setAction("MySQL server is not running in chroot environment.")
                    
            # check 'datadir' of mysql database and make sure that it is on a separate partition
            if audit_item['name'].find('check if database is not on system partition')>=0:
                #print result
                if result['datadir'] == '/':
                        config.logger.error("MySQL database is installed on a root partition. This makes it susceptible to denial of service attack via exhaustion of disk space.")
                else:
                    # find the root directory
                    mount_point = result['datadir'].split('/')[1]
                    mount_flag = False
                    # check if it is a seperate partition using "df" command
                    partition_info = os.popen('df').readlines()
                    for item in partition_info:
                        # check if root & mount directory are same
                        if item.find(mount_point)>=0:
                            mount_flag = True
                            break;
                    if not mount_flag:
                        config.logger.error("MySQL database is installed on a root partition. This makes it susceptible to denial of service attack via exhaustion of disk space.")
                    else:
                        config.setAction("MySQL database datadir is - %s"%result['datadir'])

            # check if error logging enabled
            if audit_item['name'].find('find whether error logging enabled')>=0:
                #print result
                if result['log_error']:
                    config.setAction("MySQL error logs are available here -%s"%result['log_error'])
                else:
                    config.logger.error("MySQL error logging is not enabled. Error logs must be enabled as it increases the chances to detect malicious attempt.") 
            # check whether user `root` exists
            if audit_item['name'].find('find whether user name root exist')>=0:
                #print result
                if result['root']:
                    config.setAction("User - %s exists on MySQL database server"%result['root'])
                else:
                    config.logger.error("User- root exist on MySQL database server. Disabling root user's ability to interact with MySQL will limit the use of this sensitive account for non-operating system adminstrative purposes.")
            # check if user passwords are sufficiently strong
            if audit_item['name'].find('check if user passwords are sufficiently strong')>=0:
                #print result
                if not result:
                    config.setAction("All the password hashes are at least 41 bytes long.")
                else:
                    config.logger.error("All password hashes should be 41 bytes or longer")
            
            # check if there exists wildcards in hostnames
            if audit_item['name'].find('check if there exists wildcards in hostnames')>=0:
                if not result:
                    config.setAction("It's good to see that there are no wildcards in the hostnames")
                else:
                    config.logger.error("Avoiding the use of wildcards within hostnames will ensure that only trusted hosts are capable of interacting with MySQL DB")
            
            # check whether local infile is disabled or not
            if audit_item['name'].find('check whether local infile is disabled or not')>=0:
                if result:
                    if result['local_infile'].lower() == 'off':
                        config.setAction("local_infile setting is OFF. This is good from security point of view. ")
                    else:
                        config.logger.error("'local_infile' setting is 'ON' and it is not good from security point of view. Please refer to MySQL security documentation.")
                else:
                    config.logger.error("'local_infile' setting is not applicable for this MySQL version. Ignoring this test result.")
            
            # check whether old password hashing is disabled or not
            if audit_item['name'].find('check whether old password hashing is disabled or not')>=0:
                if result:
                    if result['old_passwords'].lower() == 'off':
                        config.setAction("'old_passwords' setting is OFF. This is good from security point of view.")
                    else:
                        config.logger.error("'old_passwords' setting is 'ON' and it is not good from security point of view. Please refer to MySQL security documentation.")
                else:
                    config.logger.info("'old_passwords' setting is not applicable for this MySQL version. Ignoring this test result.")
            
            # check whether 'safe_show_database' setting is enabled or not
            if audit_item['name'].find("check whether 'safe_show_database' setting is enabled or not")>=0:
                #print result
                if result:
                    if result['safe_show_database'].lower() == 'on':
                        config.setAction("'safe_show_database' setting is ON. This is good from security point of view.")
                    else:
                        config.logger.error("'safe_show_database' setting is 'OFF' and it is not good from security point of view. Please refer to MySQL security documentation.")
                else:
                    config.logger.info("'safe_show_database' setting is not applicable for this MySQL version. Ignoring this test result.")                
                    
            # find accounts with FILE privilege
            if audit_item['name'].find('find accounts with FILE privilege')>=0:
                if result:
                    config.setAction("The following accounts were found to be having FILE privileges")
                    for item in result:
                        config.setAction("%s"%item)
                else:
                    config.logger.info("No accounts with FILE privileges were found")
            
            # find accounts with Global GRANT privilege
            if audit_item['name'].find('find accounts with Global GRANT privilege')>=0:
                if result:
                    config.setAction("The following accounts were found to be having GRANT privileges")
                    for item in result:
                        config.setAction("%s"%item)
                else:
                    config.logger.info("No accounts with GRANT privileges were found")
            
            # check whether secure auth variable is enabled
            if audit_item['name'].find('check whether secure auth variable is enabled')>=0:
                if result:
                    if result['secure_auth'].lower()=='off':
                        config.logger.error("'secure_auth' setting is OFF and it's not good from security point of view. Please refer to MySQL security documentation.")
                    else:
                        config.setAction("'secure_auth' setting is ON. This is good from security point of view.")
            
            # check whether skip_grant_tables is disabled or not.
            if audit_item['name'].find('check whether skip_grant_tables is disabled or not')>=0:
                if result:
                    if result['skip_grant_tables'].lower()=='on':
                        config.logger.error("'skip_grant_tables' setting is ON and it's not good from security point of view. Please refer to MySQL security documentation.")
                    else:
                        config.setAction("'skip_grant_tables' setting is OFF. This is good from security point of view.")
                else:
                    config.setAction("'skip_grant_tables' setting is not present in MySQL configuration.")
            
            #check whether 'merge engine' is disabled or not
            if audit_item['name'].find("check whether 'merge engine' is disabled or not") >=0:
                if result:
                    if result['merge_engine'].lower()=='disabled':
                        config.logger.error("'merge_engine' setting is disabled and it's good from security point of view.")
                    else:
                        config.setAction("'merge_engine' setting is not 'DISABLED'. This is not good from security point of view.  Please refer to MySQL security documentation.")
                else:
                    config.setAction("'merge_engine' setting is not present in MySQL configuration.")
                       
            # check whether 'skip_networking' setting is ON or not
            if audit_item['name'].find("check whether 'skip_networking' setting is ON or not")>=0:
                if result:
                    if result['skip_networking'].lower()=='off':
                        config.logger.error("'skip_networking' setting is OFF and it's good from security point of view.")
                    else:
                        config.setAction("'skip_networking' setting is ON. This is not good from security point of view.  Please refer to MySQL security documentation.")
                else:
                    config.setAction("'skip_networking' setting is not present in MySQL configuration.")
            
            # find whether have_symlink' setting is disabled or not
            if audit_item['name'].find("find whether 'have_symlink' setting is disabled or not")>=0:
                if result:
                    if result['have_symlink'].lower()=='no':
                        config.setAction("'have_symlink' setting is disabled and it's good from security point of view.")
                    else:
                        config.logger.error("'have_symlink' setting is not disabled. This is not good from security point of view.  Please refer to MySQL security documentation.")
                else:
                    config.setAction("'have_symlink' setting is not present in MySQL configuration.")
            
            config.setAction("Security audit test '%s' is now completed."%audit_item['name'])
                    
        config.setAction("Mysql security audit tests are over.")
        
        config.setAction("Disconnecting from MySQL database")
        cursor.close()
        conn.close()
        sys.exit(1)
    except Exception, info:
        print info